home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / unix / tkUnixXId.c < prev   
Encoding:
C/C++ Source or Header  |  1997-08-15  |  14.7 KB  |  538 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tkUnixXId.c --
  3.  *
  4.  *    This file provides a replacement function for the default X
  5.  *    resource allocator (_XAllocID).  The problem with the default
  6.  *    allocator is that it never re-uses ids, which causes long-lived
  7.  *    applications to crash when X resource identifiers wrap around.
  8.  *    The replacement functions in this file re-use old identifiers
  9.  *    to prevent this problem.
  10.  *
  11.  *    The code in this file is based on similar implementations by
  12.  *    George C. Kaplan and Michael Hoegeman.
  13.  *
  14.  * Copyright (c) 1993 The Regents of the University of California.
  15.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  16.  *
  17.  * See the file "license.terms" for information on usage and redistribution
  18.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  19.  *
  20.  * SCCS: @(#) tkUnixXId.c 1.22 97/06/25 13:16:47
  21.  */
  22.  
  23. /*
  24.  * The definition below is needed on some systems so that we can access
  25.  * the resource_alloc field of Display structures in order to replace
  26.  * the resource allocator.
  27.  */
  28.  
  29. #define XLIB_ILLEGAL_ACCESS 1
  30.  
  31. #include "tkInt.h"
  32. #include "tkPort.h"
  33. #include "tkUnixInt.h"
  34.  
  35. /*
  36.  * A structure of the following type is used to hold one or more
  37.  * available resource identifiers.  There is a list of these structures
  38.  * for each display.
  39.  */
  40.  
  41. #define IDS_PER_STACK 10
  42. typedef struct TkIdStack {
  43.     XID ids[IDS_PER_STACK];        /* Array of free identifiers. */
  44.     int numUsed;            /* Indicates how many of the entries
  45.                      * in ids are currently in use. */
  46.     TkDisplay *dispPtr;            /* Display to which ids belong. */
  47.     struct TkIdStack *nextPtr;        /* Next bunch of free identifiers
  48.                      * for the same display. */
  49. } TkIdStack;
  50.  
  51. /*
  52.  * Forward declarations for procedures defined in this file:
  53.  */
  54.  
  55. static XID        AllocXId _ANSI_ARGS_((Display *display));
  56. static Tk_RestrictAction CheckRestrictProc _ANSI_ARGS_((
  57.                 ClientData clientData, XEvent *eventPtr));
  58. static void        WindowIdCleanup _ANSI_ARGS_((ClientData clientData));
  59. static void        WindowIdCleanup2 _ANSI_ARGS_((ClientData clientData));
  60.  
  61. /*
  62.  *----------------------------------------------------------------------
  63.  *
  64.  * TkInitXId --
  65.  *
  66.  *    This procedure is called to initialize the id allocator for
  67.  *    a given display.
  68.  *
  69.  * Results:
  70.  *    None.
  71.  *
  72.  * Side effects:
  73.  *    The official allocator for the display is set up to be Tk_AllocXID.
  74.  *
  75.  *----------------------------------------------------------------------
  76.  */
  77.  
  78. void
  79. TkInitXId(dispPtr)
  80.     TkDisplay *dispPtr;            /* Tk's information about the
  81.                      * display. */
  82. {
  83.     dispPtr->idStackPtr = NULL;
  84.     dispPtr->defaultAllocProc = (XID (*) _ANSI_ARGS_((Display *display))) 
  85.             dispPtr->display->resource_alloc;
  86.     dispPtr->display->resource_alloc = AllocXId;
  87.     dispPtr->windowStackPtr = NULL;
  88.     dispPtr->idCleanupScheduled = 0;
  89. }
  90.  
  91. /*
  92.  *----------------------------------------------------------------------
  93.  *
  94.  * AllocXId --
  95.  *
  96.  *    This procedure is invoked by Xlib as the resource allocator
  97.  *    for a display.
  98.  *
  99.  * Results:
  100.  *    The return value is an X resource identifier that isn't currently
  101.  *    in use.
  102.  *
  103.  * Side effects:
  104.  *    The identifier is removed from the stack of free identifiers,
  105.  *    if it was previously on the stack.
  106.  *
  107.  *----------------------------------------------------------------------
  108.  */
  109.  
  110. static XID
  111. AllocXId(display)
  112.     Display *display;            /* Display for which to allocate. */
  113. {
  114.     TkDisplay *dispPtr;
  115.     TkIdStack *stackPtr;
  116.  
  117.     /*
  118.      * Find Tk's information about the display.
  119.      */
  120.  
  121.     dispPtr = TkGetDisplay(display);
  122.     
  123.     /*
  124.      * If the topmost chunk on the stack is empty then free it.  Then
  125.      * check for a free id on the stack and return it if it exists.
  126.      */
  127.  
  128.     stackPtr = dispPtr->idStackPtr;
  129.     if (stackPtr != NULL) {
  130.     while (stackPtr->numUsed == 0) {
  131.         dispPtr->idStackPtr = stackPtr->nextPtr;
  132.         ckfree((char *) stackPtr);
  133.         stackPtr = dispPtr->idStackPtr;
  134.         if (stackPtr == NULL) {
  135.         goto defAlloc;
  136.         }
  137.     }
  138.     stackPtr->numUsed--;
  139.     return stackPtr->ids[stackPtr->numUsed];
  140.     }
  141.  
  142.     /*
  143.      * No free ids in the stack:  just get one from the default
  144.      * allocator.
  145.      */
  146.  
  147.     defAlloc:
  148.     return (*dispPtr->defaultAllocProc)(display);
  149. }
  150.  
  151. /*
  152.  *----------------------------------------------------------------------
  153.  *
  154.  * Tk_FreeXId --
  155.  *
  156.  *    This procedure is called to indicate that an X resource identifier
  157.  *    is now free.
  158.  *
  159.  * Results:
  160.  *    None.
  161.  *
  162.  * Side effects:
  163.  *    The identifier is added to the stack of free identifiers for its
  164.  *    display, so that it can be re-used.
  165.  *
  166.  *----------------------------------------------------------------------
  167.  */
  168.  
  169. void
  170. Tk_FreeXId(display, xid)
  171.     Display *display;            /* Display for which xid was
  172.                      * allocated. */
  173.     XID xid;                /* Identifier that is no longer
  174.                      * in use. */
  175. {
  176.     TkDisplay *dispPtr;
  177.     TkIdStack *stackPtr;
  178.  
  179.     /*
  180.      * Find Tk's information about the display.
  181.      */
  182.  
  183.     dispPtr = TkGetDisplay(display);
  184.  
  185.     /*
  186.      * Add a new chunk to the stack if the current chunk is full.
  187.      */
  188.     
  189.     stackPtr = dispPtr->idStackPtr;
  190.     if ((stackPtr == NULL) || (stackPtr->numUsed >= IDS_PER_STACK)) {
  191.     stackPtr = (TkIdStack *) ckalloc(sizeof(TkIdStack));
  192.     stackPtr->numUsed = 0;
  193.     stackPtr->dispPtr = dispPtr;
  194.     stackPtr->nextPtr = dispPtr->idStackPtr;
  195.     dispPtr->idStackPtr = stackPtr;
  196.     }
  197.  
  198.     /*
  199.      * Add the id to the current chunk.
  200.      */
  201.  
  202.     stackPtr->ids[stackPtr->numUsed] = xid;
  203.     stackPtr->numUsed++;
  204. }
  205.  
  206. /*
  207.  *----------------------------------------------------------------------
  208.  *
  209.  * TkFreeWindowId --
  210.  *
  211.  *    This procedure is invoked instead of TkFreeXId for window ids.
  212.  *    See below for the reason why.
  213.  *
  214.  * Results:
  215.  *    None.
  216.  *
  217.  * Side effects:
  218.  *    The id given by w will eventually be freed, so that it can be
  219.  *    reused for other resources.
  220.  *
  221.  * Design:
  222.  *    Freeing window ids is very tricky because there could still be
  223.  *    events pending for a window in the event queue (or even in the
  224.  *    server) at the time the window is destroyed.  If the window
  225.  *    id were to get reused immediately for another window, old
  226.  *    events could "drop in" on the new window, causing unexpected
  227.  *    behavior.
  228.  *
  229.  *    Thus we have to wait to re-use a window id until we know that
  230.  *    there are no events left for it.  Right now this is done in
  231.  *    two steps.  First, we wait until we know that the server
  232.  *    has seen the XDestroyWindow request, so we can be sure that
  233.  *    it won't generate more events for the window and that any
  234.  *    existing events are in our queue.  Second, we make sure that
  235.  *    there are no events whatsoever in our queue (this is conservative
  236.  *    but safe).
  237.  *
  238.  *     The first step is done by remembering the request id of the
  239.  *    XDestroyWindow request and using LastKnownRequestProcessed to
  240.  *    see what events the server has processed.  If multiple windows
  241.  *    get destroyed at about the same time, we just remember the
  242.  *    most recent request number for any of them (again, conservative
  243.  *    but safe).
  244.  *
  245.  *    There are a few other complications as well.  When Tk destroys a
  246.  *    sub-tree of windows, it only issues a single XDestroyWindow call,
  247.  *    at the very end for the root of the subtree.  We can't free any of
  248.  *    the window ids until the final XDestroyWindow call.  To make sure
  249.  *    that this happens, we have to keep track of deletions in progress,
  250.  *    hence the need for the "destroyCount" field of the display.
  251.  *
  252.  *    One final problem.  Some servers, like Sun X11/News servers still
  253.  *    seem to have problems with ids getting reused too quickly.  I'm
  254.  *    not completely sure why this is a problem, but delaying the
  255.  *    recycling of ids appears to eliminate it.  Therefore, we wait
  256.  *    an additional few seconds, even after "the coast is clear"
  257.  *    before reusing the ids.
  258.  *
  259.  *----------------------------------------------------------------------
  260.  */
  261.  
  262. void
  263. TkFreeWindowId(dispPtr, w)
  264.     TkDisplay *dispPtr;        /* Display that w belongs to. */
  265.     Window w;            /* X identifier for window on dispPtr. */
  266. {
  267.     TkIdStack *stackPtr;
  268.  
  269.     /*
  270.      * Put the window id on a separate stack of window ids, rather
  271.      * than the main stack, so it won't get reused right away.  Add
  272.      * a new chunk to the stack if the current chunk is full.
  273.      */
  274.  
  275.     stackPtr = dispPtr->windowStackPtr;
  276.     if ((stackPtr == NULL) || (stackPtr->numUsed >= IDS_PER_STACK)) {
  277.     stackPtr = (TkIdStack *) ckalloc(sizeof(TkIdStack));
  278.     stackPtr->numUsed = 0;
  279.     stackPtr->dispPtr = dispPtr;
  280.     stackPtr->nextPtr = dispPtr->windowStackPtr;
  281.     dispPtr->windowStackPtr = stackPtr;
  282.     }
  283.  
  284.     /*
  285.      * Add the id to the current chunk.
  286.      */
  287.  
  288.     stackPtr->ids[stackPtr->numUsed] = w;
  289.     stackPtr->numUsed++;
  290.  
  291.     /*
  292.      * Schedule a call to WindowIdCleanup if one isn't already
  293.      * scheduled.
  294.      */
  295.  
  296.     if (!dispPtr->idCleanupScheduled) {
  297.     dispPtr->idCleanupScheduled = 1;
  298.     Tcl_CreateTimerHandler(100, WindowIdCleanup, (ClientData) dispPtr);
  299.     }
  300. }
  301.  
  302. /*
  303.  *----------------------------------------------------------------------
  304.  *
  305.  * WindowIdCleanup --
  306.  *
  307.  *    See if we can now free up all the accumulated ids of
  308.  *    deleted windows.
  309.  *
  310.  * Results:
  311.  *    None.
  312.  *
  313.  * Side effects:
  314.  *    If it's safe to move the window ids back to the main free
  315.  *    list, we schedule this to happen after a few mores seconds
  316.  *    of delay.  If it's not safe to move them yet, a timer handler
  317.  *    gets invoked to try again later.
  318.  *
  319.  *----------------------------------------------------------------------
  320.  */
  321.  
  322. static void
  323. WindowIdCleanup(clientData)
  324.     ClientData clientData;    /* Pointer to TkDisplay for display */
  325. {
  326.     TkDisplay *dispPtr = (TkDisplay *) clientData;
  327.     int anyEvents, delta;
  328.     Tk_RestrictProc *oldProc;
  329.     ClientData oldData;
  330.     static Tcl_Time timeout = {0, 0};
  331.  
  332.     dispPtr->idCleanupScheduled = 0;
  333.  
  334.     /*
  335.      * See if it's safe to recycle the window ids.  It's safe if:
  336.      * (a) no deletions are in progress.
  337.      * (b) the server has seen all of the requests up to the last
  338.      *     XDestroyWindow request.
  339.      * (c) there are no events in the event queue; the only way to
  340.      *     test for this right now is to create a restrict proc that
  341.      *     will filter the events, then call Tcl_DoOneEvent to see if
  342.      *       the procedure gets invoked.
  343.      */
  344.  
  345.     if (dispPtr->destroyCount > 0) {
  346.     goto tryAgain;
  347.     }
  348.     delta = LastKnownRequestProcessed(dispPtr->display)
  349.         - dispPtr->lastDestroyRequest;
  350.     if (delta < 0) {
  351.     XSync(dispPtr->display, False);
  352.     }
  353.     anyEvents = 0;
  354.     oldProc = Tk_RestrictEvents(CheckRestrictProc, (ClientData) &anyEvents,
  355.         &oldData);
  356.     TkUnixDoOneXEvent(&timeout);
  357.     Tk_RestrictEvents(oldProc, oldData, &oldData);
  358.     if (anyEvents) {
  359.     goto tryAgain;
  360.     }
  361.  
  362.     /*
  363.      * These ids look safe to recycle, but we still need to delay a bit
  364.      * more (see comments for TkFreeWindowId).  Schedule the final freeing.
  365.      */
  366.  
  367.     if (dispPtr->windowStackPtr != NULL) {
  368.     Tcl_CreateTimerHandler(5000, WindowIdCleanup2,
  369.         (ClientData) dispPtr->windowStackPtr);
  370.     dispPtr->windowStackPtr = NULL;
  371.     }
  372.     return;
  373.  
  374.     /*
  375.      * It's still not safe to free up the ids.  Try again a bit later.
  376.      */
  377.  
  378.     tryAgain:
  379.     dispPtr->idCleanupScheduled = 1;
  380.     Tcl_CreateTimerHandler(500, WindowIdCleanup, (ClientData) dispPtr);
  381. }
  382.  
  383. /*
  384.  *----------------------------------------------------------------------
  385.  *
  386.  * WindowIdCleanup2 --
  387.  *
  388.  *    This procedure is the last one in the chain that recycles
  389.  *    window ids.  It takes all of the ids indicated by its
  390.  *    argument and adds them back to the main id free list.
  391.  *
  392.  * Results:
  393.  *    None.
  394.  *
  395.  * Side effects:
  396.  *    Window ids get added to the main free list for their display.
  397.  *
  398.  *----------------------------------------------------------------------
  399.  */
  400.  
  401. static void
  402. WindowIdCleanup2(clientData)
  403.     ClientData clientData;    /* Pointer to TkIdStack list. */
  404. {
  405.     TkIdStack *stackPtr = (TkIdStack *) clientData;
  406.     TkIdStack *lastPtr;
  407.  
  408.     lastPtr = stackPtr;
  409.     while (lastPtr->nextPtr != NULL) {
  410.     lastPtr = lastPtr->nextPtr;
  411.     }
  412.     lastPtr->nextPtr = stackPtr->dispPtr->idStackPtr;
  413.     stackPtr->dispPtr->idStackPtr = stackPtr;
  414. }
  415.  
  416. /*
  417.  *----------------------------------------------------------------------
  418.  *
  419.  * CheckRestrictProc --
  420.  *
  421.  *    This procedure is a restrict procedure, called by Tcl_DoOneEvent
  422.  *    to filter X events.  All it does is to set a flag to indicate
  423.  *    that there are X events present.
  424.  *
  425.  * Results:
  426.  *    Sets the integer pointed to by the argument, then returns
  427.  *    TK_DEFER_EVENT.
  428.  *
  429.  * Side effects:
  430.  *    None.
  431.  *
  432.  *----------------------------------------------------------------------
  433.  */
  434.  
  435. static Tk_RestrictAction
  436. CheckRestrictProc(clientData, eventPtr)
  437.     ClientData clientData;    /* Pointer to flag to set. */
  438.     XEvent *eventPtr;        /* Event to filter;  not used. */
  439. {
  440.     int *flag = (int *) clientData;
  441.     *flag = 1;
  442.     return TK_DEFER_EVENT;
  443. }
  444.  
  445. /*
  446.  *----------------------------------------------------------------------
  447.  *
  448.  * Tk_GetPixmap --
  449.  *
  450.  *    Same as the XCreatePixmap procedure except that it manages
  451.  *    resource identifiers better.
  452.  *
  453.  * Results:
  454.  *    Returns a new pixmap.
  455.  *
  456.  * Side effects:
  457.  *    None.
  458.  *
  459.  *----------------------------------------------------------------------
  460.  */
  461.  
  462. Pixmap
  463. Tk_GetPixmap(display, d, width, height, depth)
  464.     Display *display;        /* Display for new pixmap. */
  465.     Drawable d;            /* Drawable where pixmap will be used. */
  466.     int width, height;        /* Dimensions of pixmap. */
  467.     int depth;            /* Bits per pixel for pixmap. */
  468. {
  469.     return XCreatePixmap(display, d, (unsigned) width, (unsigned) height,
  470.         (unsigned) depth);
  471. }
  472.  
  473. /*
  474.  *----------------------------------------------------------------------
  475.  *
  476.  * Tk_FreePixmap --
  477.  *
  478.  *    Same as the XFreePixmap procedure except that it also marks
  479.  *    the resource identifier as free.
  480.  *
  481.  * Results:
  482.  *    None.
  483.  *
  484.  * Side effects:
  485.  *    The pixmap is freed in the X server and its resource identifier
  486.  *    is saved for re-use.
  487.  *
  488.  *----------------------------------------------------------------------
  489.  */
  490.  
  491. void
  492. Tk_FreePixmap(display, pixmap)
  493.     Display *display;        /* Display for which pixmap was allocated. */
  494.     Pixmap pixmap;        /* Identifier for pixmap. */
  495. {
  496.     XFreePixmap(display, pixmap);
  497.     Tk_FreeXId(display, (XID) pixmap);
  498. }
  499.  
  500. /*
  501.  *----------------------------------------------------------------------
  502.  *
  503.  * TkpWindowWasRecentlyDeleted --
  504.  *
  505.  *    Checks whether the window was recently deleted. This is called
  506.  *    by the generic error handler to detect asynchronous notification
  507.  *    of errors due to operations by Tk on a window that was already
  508.  *    deleted by the server.
  509.  *
  510.  * Results:
  511.  *    1 if the window was deleted recently, 0 otherwise.
  512.  *
  513.  * Side effects:
  514.  *    None.
  515.  *
  516.  *----------------------------------------------------------------------
  517.  */
  518.  
  519. int
  520. TkpWindowWasRecentlyDeleted(win, dispPtr)
  521.     Window win;        /* The window to check for. */
  522.     TkDisplay *dispPtr;    /* The window belongs to this display. */
  523. {
  524.     TkIdStack *stackPtr;
  525.     int i;
  526.  
  527.     for (stackPtr = dispPtr->windowStackPtr;
  528.          stackPtr != NULL;
  529.          stackPtr = stackPtr->nextPtr) {
  530.         for (i = 0; i < stackPtr->numUsed; i++) {
  531.             if ((Window) stackPtr->ids[i] == win) {
  532.                 return 1;
  533.             }
  534.         }
  535.     }
  536.     return 0;
  537. }
  538.